package com.demo.boot.commmon.aop;

import java.io.Serializable;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

@Aspect
@Configuration
public class MethodCacheInterceptor implements InitializingBean {
	
	@Value("${ehcache.enableMethodCache}")
	private boolean enableMethodCache;
	
	private Cache cache;

	public void setCache(Cache cache) {
		this.cache = cache;
	}
	
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(cache, "A cache is required. Use setCache(Cache) to provide one.");
	}
	
	@Pointcut("execution(* com.demo.boot.repositories.*.*(..))")
	public void pc(){}

	@SuppressWarnings("deprecation")
	@Around(value="pc()")
	public Object invoke(ProceedingJoinPoint invocation) throws Throwable {
		if(enableMethodCache){
			String targetName = invocation.getTarget().getClass().getName();
			String methodName = invocation.getSignature().getName();
			Object[] arguments = invocation.getArgs();
			Object result;
			String cacheKey = getCacheKey(targetName, methodName, arguments);
			Element element = cache.get(cacheKey);
			if (element == null) {
				result = invocation.proceed();
				element = new Element(cacheKey, (Serializable) result);
				cache.put(element);
			}
			return element.getValue();
		}else{
			return invocation.proceed();
		}
	}

	@Pointcut("execution(* com.demo.boot.controller.*.*(..))")
	public void cpc(){}
	
	@SuppressWarnings("deprecation")
	@Around(value="cpc()")
	public Object invoke2(ProceedingJoinPoint invocation) throws Throwable {
		if(enableMethodCache){
			String targetName = invocation.getTarget().getClass().getName();
			String methodName = invocation.getSignature().getName();
			Object[] arguments = invocation.getArgs();
			Object result;
			String cacheKey = getCacheKey(targetName, methodName, arguments);
			Element element = cache.get(cacheKey);
			if (element == null) {
				result = invocation.proceed();
				element = new Element(cacheKey, (Serializable) result);
				cache.put(element);
			}
			return element.getValue();
		}else{
			return invocation.proceed();
		}
	}
	
	
	private String getCacheKey(String targetName, String methodName, Object[] arguments) {
		StringBuffer sb = new StringBuffer();
		sb.append(targetName).append(".").append(methodName);
		if ((arguments != null) && (arguments.length != 0)) {
			for (int i = 0; i < arguments.length; i++) {
				sb.append(".").append(arguments[i]);
			}
		}

		return sb.toString();
	}
}